#include "ws2812b.h"
#include "stm32f4xx_hal.h"
#include "FreeRTOS.h"
#include "task.h"
#include "math.h"
#include "tim.h"
#include "log.h"

const static char *TAG = "ws2812b.c";

/* DMA data cache */
static uint16_t pwmData[(24 * MAX_LED_NUM) + 50];

/**
 * @brief Set the led object
 *
 * @param ws2812
 * @param led_num
 * @param Red
 * @param Green
 * @param Blue
 */
void set_led(WS2812B *ws2812, uint8_t led_num, uint8_t Red, uint8_t Green, uint8_t Blue)
{
    ws2812->LED_Data[led_num][0] = led_num;
    ws2812->LED_Data[led_num][1] = Green;
    ws2812->LED_Data[led_num][2] = Red;
    ws2812->LED_Data[led_num][3] = Blue;
    MY_LOGI(TAG, "set %d led finish!\r\n", led_num);
}

/**
 * @brief set_brightness
 *
 * @param ws2812
 * @param brightness
 */
void set_brightness(WS2812B *ws2812, int brightness) // 0-45
{
#if USE_BRIGHTNESS
    if (brightness > 45)
        brightness = 45;
    for (int i = 0; i < MAX_LED_NUM; i++)
    {
        ws2812->LED_Mod[i][0] = ws2812->LED_Data[i][0];
        for (int j = 1; j < 4; j++)
        {
            double angle = 90 - brightness; // in degrees
            angle = (angle * PI / 180);     // in rad
            ws2812->LED_Mod[i][j] = (ws2812->LED_Data[i][j]) / (tan(angle));
        }
    }
#endif
}

/**
 * @brief ws2812_send_dma
 *
 * @param htim TIM PWM handle
 * @param Channel TIM Channels to be enabled
 * @param ws2812 WS2812's struct
 */
void ws2812_send_dma(TIM_HandleTypeDef *htim, uint32_t Channel, WS2812B *ws2812)
{
    uint32_t indx = 0;
    uint32_t color;
    for (int i = 0; i < MAX_LED_NUM; i++)
    {
#if USE_BRIGHTNESS
        color = ((ws2812->LED_Mod[i][1] << 16) | (ws2812->LED_Mod[i][2] << 8) | (ws2812->LED_Mod[i][3])); /* DMA send data struct -> G[7:0]-R[7:0]-B[7:0] */
#else
        color = ((ws2812->LED_Data[i][1] << 16) | (ws2812->LED_Data[i][2] << 8) | (ws2812->LED_Data[i][3])); /* DMA send data struct -> G[7:0]-R[7:0]-B[7:0] */
#endif
        for (int i = 23; i >= 0; i--)
        {
            if (color & (1 << i)) /* 0x0000 0001 */
            {
                pwmData[indx] = 80; /* 2/3 of 120 */
            }
            else
                pwmData[indx] = 40; /* 1/3 of 120 */
            indx++;
        }
    }
    /* soft reset ws2812 is >50us  */
    for (int i = 0; i < 50; i++)
    {
        pwmData[indx] = 0;
        indx++;
    }
    /* start send data to DMA */
    HAL_TIM_PWM_Start_DMA(htim, Channel, (uint32_t *)pwmData, indx);
    while (htim->State == HAL_TIM_STATE_BUSY)
        ;
}

/**
 * @brief breathing_lamps_demo - period is 20*70*2ms=2.8s
 *
 * @param htim TIM PWM handle
 * @param Channel TIM Channels to be enabled
 * @param ws2812 WS2812's struct
 */
void breathing_lamps_demo(TIM_HandleTypeDef *htim, uint32_t Channel, WS2812B *ws2812)
{
    for (int i = 0; i < 70; i++)
    {
        set_brightness(ws2812, i);
        ws2812_send_dma(htim, Channel, ws2812);
        vTaskDelay(pdMS_TO_TICKS(20));
    }
    for (int i = 70; i >= 0; i--)
    {
        set_brightness(ws2812, i);
        ws2812_send_dma(htim, Channel, ws2812);
        vTaskDelay(pdMS_TO_TICKS(20));
    }
}

/*
    定时器每个周期伟1.25us -> 灯珠的一个码型周期是1.25us
    码型0 -> 高电平占1/3 低电平占2/3
    码型1 -> 高电平占2/3 低电平占1/3
    复位只需无操作280us以上即可

    控制亮度其实就是控制每个颜色的数据大小，每个颜色都占8位，也就在0-255之间
    但是，如果要整体改亮度那就RGB三个颜色一起改。
    对于多个RGB串级灯珠，手写单个效果估计比较困难，可以参考下面的网站进行在线预览并生成对应的RGB效果：
    https://adrianotiger.github.io/Neopixel-Effect-Generator/
*/
